/*
* @Author: 窦子滨<zibin_5257@163.com>
* @Date: 2019-04-10 10:17
 */
package TcpPack

import (
	"base/common"
	"context"
	"errors"
	"fmt"
	"net"
	"sync"
)

//连接池对象

var (
	//充电桩连接池
	ChargeConn = make(map[string]*HpCharge, 2000)
	//充电桩编号与连接池序号对应关系
	ChargeConnDeviceID = make(map[int32]string, 2000)

	//充电桩连接池 读写锁
	ChargeConnRW sync.RWMutex
	//连接池与电桩编号对应关系读写锁
	ChargeConnDeviceIDMutex sync.RWMutex
)

//恒品充电桩结构
type HpCharge struct {
	Conn      net.Conn    //连接对象
	ReadChan  chan []byte //读取通道
	WriteChan chan []byte //写入通道

	ctx    context.Context    //全局取消
	Cancel context.CancelFunc //取消处理 调用该方法会自动退出当前协程

	DeviceId int32 //电桩终端号编号

	connDeviceID sync.RWMutex //充电桩编号锁

	ConnId string //连接编号 根据一定的规则自动生成 便于在未产生数据包时保存连接池

	//ChanData      map[string]chan []byte //数据通道
	//ifInit        bool                   //是否初始化了通道数据
	//chanDataMutex sync.RWMutex           //数据通道操作锁
	chanTime      chan int //对时通道 用于对时回复检测
	chanHealth    chan int //心跳通道 用于定时检测是否心跳 超过32秒未心跳输出心跳超时信息
	chanReal      chan int //实时数据通道 用于定时检测是否在指定时间内主动上传实时数据
	chanLogin     chan int //登录数据通道 用于检测建立连接后20s内是否接收到登陆指令
	chan0x41      chan int //0x41获取浮点费率检测
	chan0x05      chan int
	chan0x30      chan int
	chan0x10start chan int
	chan0x10stop  chan int
	chan0x45      chan int
	chan0x06      chan int

	readyStart   bool //是否已经下发充电命令
	readyCard    bool //是否检测刷卡
	readyStarted bool //是否已经开始充电

	//心跳处理
	HealthLastTime int64 //最后心跳时间 时间戳 单位秒
}

//初始化一个新的连接池对象
func WriteConn(conn net.Conn) *HpCharge {
	ConnId := common.MakeUUID()
	Log.Info("[%s][][建立新的连接池对象][]", ConnId)

	h := &HpCharge{
		Conn:          conn,
		ReadChan:      make(chan []byte),
		WriteChan:     make(chan []byte),
		ConnId:        ConnId,
		chanTime:      make(chan int),
		chanHealth:    make(chan int),
		chanReal:      make(chan int),
		chanLogin:     make(chan int),
		chan0x41:      make(chan int),
		chan0x05:      make(chan int),
		chan0x30:      make(chan int),
		chan0x10start: make(chan int),
		chan0x10stop:  make(chan int),
		chan0x45:      make(chan int),
		chan0x06:      make(chan int),
		readyStart:    false,
		readyCard:     false,
		readyStarted:  false,
	}
	h.ctx, h.Cancel = context.WithCancel(context.Background())
	ChargeConnRW.Lock()
	ChargeConn[ConnId] = h
	ChargeConnRW.Unlock()

	return h
}

//设置当前连接池的设备编号
func (c *HpCharge) SetConnDeviceID(DeviceID int32) {
	c.connDeviceID.RLock()
	device_id := c.DeviceId
	c.connDeviceID.RUnlock()

	//未设置设备编号则进行设置
	if device_id == 0 {
		Log.Info("[%s][%d][充电桩与连接池建立关系][]", c.ConnId, DeviceID)
		c.connDeviceID.Lock()
		c.DeviceId = DeviceID
		c.connDeviceID.Unlock()

		//如果已经存在连接 则删除连接
		//conn,err:=GetConnDeviceId(DeviceID)
		//if err==nil{
		//	conn.cancel()
		//	conn.DeleteConn()
		//}

		ChargeConnDeviceIDMutex.Lock()
		ChargeConnDeviceID[DeviceID] = c.ConnId
		ChargeConnDeviceIDMutex.Unlock()
	}
	Log.Info("[][][当前连接池连接数量][%d]", len(ChargeConn))
	//fmt.Println("全部连接池：", ChargeConn, ChargeConnDeviceIDMutex)
}

//删除一个连接对象
func (conn *HpCharge) DeleteConn() {
	//println("删除连接对象")
	//删除连接池对象
	ChargeConnRW.Lock()
	delete(ChargeConn, conn.ConnId)
	ChargeConnRW.Unlock()
}

//获取一个连接对象
func GetConn(ConnID string) (*HpCharge, error) {
	ChargeConnRW.RLock()
	data, ok := ChargeConn[ConnID]
	ChargeConnRW.RUnlock()
	if (!ok) {
		return nil, errors.New("连接对象不存在")
	}
	return data, nil
}

/**
通过充电桩编号获取连接序号
*/
func GetConnId(d int32) (string, error) {
	//connId := ""
	ChargeConnDeviceIDMutex.RLock()
	connId, ok := ChargeConnDeviceID[d]
	ChargeConnDeviceIDMutex.RUnlock()

	//ChargeConnDeviceIDMutex.RUnlock()

	if !ok {
		//fmt.Println("连接池对象不存在")
		return "", errors.New("该设备未与服务器建立连接[" + fmt.Sprintf("%d", d) + "]")
	}

	return connId, nil
}

//使用设备编号获取连接池对象
func GetConnByDeviceId(id int32) (*HpCharge, error) {
	//fmt.Println("通过设备编号获取连接池对象")
	connId, err := GetConnId(id)
	//fmt.Println("连接池名称：", connId)
	println(err)
	if err != nil {
		return nil, err
	}
	//fmt.Println("获取连接对象")
	conn, err := GetConn(connId)
	if err != nil {
		return nil, err
	}

	return conn, nil

}

//使用设备编号删除一个连接池对象
func RmConnDeviceId(id int32) {
	ChargeConnRW.RLock()
	defer ChargeConnRW.RUnlock()
	for k, v := range ChargeConn {
		//fmt.Println("连接池对象：", v)
		//fmt.Println("连接池名称：", k)
		if v.DeviceId == id {
			delete(ChargeConn, k)
		}
	}

}

//检测设备编号是否存在连接池对象和登录请求
func CheckDeviceIdConn(id int32) (bool) {
	var r bool = false
	ChargeConnDeviceIDMutex.RLock()
	defer ChargeConnDeviceIDMutex.RUnlock()
	if _, ok := ChargeConnDeviceID[id]; ok {
		r = true
	}

	return r
}
